/* SD card and FAT filesystem example.
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

// This example uses SPI peripheral to communicate with SD card.

#include "esp_log.h"
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"

#include "bsp_sdspi.h"

// #define TAG  "bsp_sdspi"
 
// Pin mapping
#define PIN_NUM_CS   41
#define PIN_NUM_MOSI 40
#define PIN_NUM_CLK  39
#define PIN_NUM_MISO 38

#if 1
#define SPI_DMA_CHAN    SPI_DMA_CH_AUTO
#else
#define SPI_DMA_CHAN    1
#endif

// 打印文件目录
void print_file_directory(void)
{
#ifdef TAG    
    uint8_t i = 0;
    DIR *dir = NULL;  
    struct dirent *entry;  
       
    if ( (dir = opendir(MOUNT_POINT)) == NULL) {  
        printf("opendir failed!");  
        return;  
    }

    while ( (entry = readdir(dir)) != NULL) {  
        printf("filename[%d] = %s | %d\r\n", i++, entry->d_name, entry->d_type);  // 输出文件或者目录的名称, 文件类型   
    }  

    closedir(dir);     
#endif    
}

// 输出MP3音乐名
void mp3_print_file_name(void)
{
#ifdef TAG    
    uint8_t i = 0;
    DIR *dir = NULL;  
    struct dirent *entry;  
       
    if ( (dir = opendir(MOUNT_POINT"/AUDIO")) == NULL) {  
        printf("opendir failed!");  
        return;  
    }

    while ( (entry = readdir(dir)) != NULL) {  
        printf("AUDIO[%d] = %s | %d\r\n", i++, entry->d_name, entry->d_type);  // 输出文件或者目录的名称, 文件类型   
    }  

    closedir(dir);     
#endif    
}


// 根据索引获取音频文件名
char *fat_open_audio_name(const char *audio_dir, uint8_t findex)
{
    /* 根据文件索引找到文件名,并拼接成完整的路径，然后打开文件 */
    DIR *dir = NULL;  
    struct dirent *entry;   
    if ( (dir = opendir(audio_dir)) == NULL) {  
        #ifdef TAG
        ESP_LOGE(TAG, "opendir failed!\n");  
        #endif
        return NULL;  
    }
    seekdir(dir, findex);
    entry = readdir(dir);
    closedir(dir);

    if (entry == NULL) {
        #ifdef TAG
        ESP_LOGE(TAG, "readdir failed!\n");  
        #endif
        return NULL;  
    }

    static char fpath[64] = { 0 };
    memset(fpath, 0, sizeof(fpath));
    uint8_t len = sprintf(fpath, "%s/", audio_dir);
    strcpy(fpath + len, entry->d_name);
    #ifdef TAG
    // ESP_LOGI(TAG, "file_fpath[%d]: %s", findex, fpath);
    #endif
    return fpath;
}


esp_err_t bsp_sdspi_init(void) 
{
    // Options for mounting the filesystem.
    // If format_if_mount_failed is set to true, SD card will be partitioned and
    // formatted in case when mounting fails.
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
        .format_if_mount_failed = true,
#else
        .format_if_mount_failed = false,
#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
        // Component config → FAT Filesystem support
        // (3) Number of simultaneously open files protected by lock function
        .max_files = CONFIG_FATFS_FS_LOCK,   // 需要menuconfig这个参数
        .allocation_unit_size = 4 * 1024
    };
   
    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
    // Please check its source code and implement error recovery when developing
    // production applications.
    #ifdef TAG
    ESP_LOGI(TAG, "Initializing SD card");
    ESP_LOGI(TAG, "Using SPI peripheral");
    #endif

    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    // host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; // Uses SPI mode and max frequency set to 20MHz
    spi_bus_config_t bus_cfg = {
        .mosi_io_num = PIN_NUM_MOSI,
        .miso_io_num = PIN_NUM_MISO,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4 * 1024,
    };
    esp_err_t ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN);
    if (ret != ESP_OK) {
        #ifdef TAG
        ESP_LOGE(TAG, "Failed to initialize bus.");
        #endif
        return ret;
    }

    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = PIN_NUM_CS;
    slot_config.host_id = host.slot;

    #ifdef TAG
    ESP_LOGI(TAG, "Mounting filesystem");
    #endif
    sdmmc_card_t *card;
    ret = esp_vfs_fat_sdspi_mount(MOUNT_POINT, &host, &slot_config, &mount_config, &card);
    if (ret != ESP_OK) {
        #ifdef TAG
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount filesystem. "
                     "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        } else {
            ESP_LOGE(TAG, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        #endif
        return ret;
    }

    #ifdef TAG
    ESP_LOGI(TAG, "Filesystem mounted succeed.");
    // Card has been initialized, print its properties
    sdmmc_card_print_info(stdout, card);
    // print_file_directory();
    mp3_print_file_name();
    #endif

    // Use POSIX and C standard library functions to work with files.
    // Check if destination file exists before renaming
    #if 0
    const char *file_foo = MOUNT_POINT"/打墙火柴人.gif";
    struct stat st;
    if (stat(file_foo, &st) == 0) {
        unlink(file_foo); // Delete it if it exists
    }
    #endif
    
    #if 0
    const char *filepath = MOUNT_POINT"/SCENE";   
    if (access(filepath, F_OK) != 0) {  /* Check for file existence */
        int res = mkdir(filepath, S_IRWXU);// 返回 0 表示创建成功，-1 表示失败
        if (res != 0) {
            #ifdef TAG
            ESP_LOGW(TAG, "FAIL mkdir %s = %d", filepath, res);
            #endif
        }
    }
    #endif

    return ret;

#if 0
    // All done, unmount partition and disable SPI peripheral
    esp_vfs_fat_sdcard_unmount(MOUNT_POINT, card);
    ESP_LOGI(TAG, "Card unmounted");

    //deinitialize the bus after all devices are removed
    spi_bus_free(host.slot);
#endif    
}
